import logging
from enum import Enum
from typing import Generic, Optional, TypeVar, Union, Any

from fastapi import Request, FastAPI
from fastapi.encoders import jsonable_encoder
from fastapi.responses import ORJSONResponse
from fastapi.exceptions import RequestValidationError
from pydantic import BaseModel, ConfigDict
from starlette.exceptions import HTTPException as StarletteHTTPException

from app.core.context import get_request_id
from app.core.exceptions import APIException
from app.core.i18n import t

logger = logging.getLogger(__name__)

T = TypeVar("T")


class ResponseCode(int, Enum):
    """Response codes with i18n keys"""

    FAIL = 0  # response.fail
    SUCCESS = 200  # response.success


class Response(BaseModel, Generic[T]):
    code: Union[ResponseCode, int] = ResponseCode.SUCCESS
    message: str = ""
    data: Optional[T] = None
    request_id: Optional[str] = None

    model_config = ConfigDict(arbitrary_types_allowed=True)

    @classmethod
    def success(
        cls,
        data: Optional[T] = None,
        message: Optional[str] = None,
        i18n_key: Optional[str] = None,
        request_id: Optional[str] = None,
    ) -> "Response[T]":
        """Success response with i18n support"""
        if i18n_key:
            message = t(i18n_key)
        elif not message:
            message = t("response.success")

        if not request_id:
            request_id = get_request_id()
        return cls(code=ResponseCode.SUCCESS, message=message, data=data, request_id=request_id)

    @classmethod
    def error(
        cls,
        message: Optional[str] = None,
        code: Union[ResponseCode, int] = ResponseCode.FAIL,
        data: Optional[T] = None,
        request_id: Optional[str] = None,
    ) -> "Response[T]":
        """Error response with i18n support"""
        if not message:
            # Try to get message from i18n key based on code if no specific key provided
            default_key = f"response.{code.name.lower()}" if isinstance(code, ResponseCode) else "response.fail"
            message = t(default_key)

        if not request_id:
            request_id = get_request_id()
        return cls(code=code, message=message, data=data, request_id=request_id)

    def to_orjson_response(self, **kwargs) -> ORJSONResponse:
        """Convert to ORJSONResponse"""
        return ORJSONResponse(content=jsonable_encoder(self.model_dump()), **kwargs)


def setup_exception_handlers(app: FastAPI) -> None:
    """Setup exception handlers with i18n support"""

    @app.exception_handler(APIException)
    async def api_exception_handler(request: Request, exc: APIException) -> ORJSONResponse:
        """Handle API exceptions"""
        return Response.error(message=exc.message, code=exc.code, data=exc.data).to_orjson_response(
            status_code=exc.status_code
        )

    @app.exception_handler(StarletteHTTPException)
    async def http_exception_handler(request: Request, exc: StarletteHTTPException) -> ORJSONResponse:
        """
        StarletteHTTPException 不是主动抛出的异常，
        而是 FastAPI 自动抛出的异常，需要根据状态码来判断翻译
        """
        i18n_key = f"response.{exc.status_code}"
        return Response.error(message=t(i18n_key), code=ResponseCode.FAIL).to_orjson_response(
            status_code=exc.status_code
        )

    @app.exception_handler(RequestValidationError)
    async def validation_exception_handler(request: Request, exc: RequestValidationError) -> ORJSONResponse:
        """
        参数验证异常，需要根据错误类型来判断翻译
        """
        errors = exc.errors()
        error_messages = []

        for error in errors:
            field = " -> ".join(str(x) for x in error["loc"])
            msg_key = f"validation.{error['type']}"
            message = t(msg_key) if msg_key else error["msg"]
            error_messages.append(f"{field}: {message}")

        return Response.error(message="\n".join(error_messages), code=ResponseCode.FAIL).to_orjson_response(
            status_code=200
        )

    @app.exception_handler(Exception)
    async def global_exception_handler(request: Request, exc: Exception) -> ORJSONResponse:
        """
        全局异常处理，需要根据错误类型来判断翻译
        """
        logger.exception("Unhandled exception occurred", exc_info=exc)

        return Response.error(message=t("response.server_error"), code=ResponseCode.FAIL).to_orjson_response(
            status_code=200
        )
